package syntax

import lexis.{CLexeme, CLexemeType}
import scala.collection.mutable
import lexis.CLexemeType._
import lexis.CLexeme

object CGrammar
{
    implicit def LexemeType2Terminal(value:CLexemeType) = new CTerminal(value)

    implicit def Lexeme2Terminal(value:CLexeme) = lexemeTypeToTerminalMap(value.lexemeType)

    object CProgram extends CStart(TERMINATOR)
    {
        CProgram := CFlow
    }

    object CFlow extends CNonTerminal
    {
        CFlow := CFlowUnit | CFlow & CFlowUnit
    }
    
    object CFlowUnit extends CNonTerminal
    {
        CFlowUnit := CWhile | CExpression
    }

    object CWhile extends CNonTerminal
    {
        CWhile :=
            WHILE & CExpression & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            WHILE & CExpression & CFlowUnit |
            WHILE & CExpression & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            WHILE & CExpression & CFlowUnit
    }

    object CIfElse extends CNonTerminal
    {
        CIfElse :=
            IF & CExpression & CFlowUnit |
            IF & CExpression & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            IF & CExpression & CFlowUnit & ELSE & CFlowUnit |
            IF & CExpression & LEFT_BRACKET & CFlow & RIGHT_BRACKET & ELSE & CFlowUnit |
            IF & CExpression & CFlowUnit & ELSE & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            IF & CExpression & LEFT_BRACKET & CFlow & RIGHT_BRACKET & ELSE & LEFT_BRACKET & CFlow & RIGHT_BRACKET
    }

    object CAssign extends CNonTerminal
    {
        CAssign := IDENTIFIER & ASSIGN & CExpression
    }

    object CExpression extends CNonTerminal
    {
        CExpression := NULL | CBooleanOr | CFunctionDefinition | CAssign | CIfElse
    }

    object CBooleanOr extends CNonTerminal
    {
        CBooleanOr := CBooleanAnd | CBooleanOr & OR & CBooleanAnd
    }

    object CBooleanAnd extends CNonTerminal
    {
        CBooleanAnd := CComparingExpression | CBooleanAnd & AND & CComparingExpression
    }

    object CComparingExpression extends CNonTerminal
    {
        CComparingExpression := CBooleanNot |
                                CBooleanNot & NEQ & CBooleanNot |
                                CBooleanNot & EQ & CBooleanNot |
                                CBooleanNot & GT & CBooleanNot |
                                CBooleanNot & GTE & CBooleanNot |
                                CBooleanNot & LT & CBooleanNot |
                                CBooleanNot & LTE & CBooleanNot
    }

    object CBooleanNot extends CNonTerminal
    {
        CBooleanNot := CAddSub | FALSE | TRUE | NOT & CAddSub | NOT & TRUE | NOT & FALSE
    }


    object CAddSub extends CNonTerminal
    {
        CAddSub := CMulDiv | CAddSub & ADD & CMulDiv | CAddSub & SUB & CMulDiv
    }

    object CMulDiv extends CNonTerminal
    {
        CMulDiv := CArithmeticExpression | CMulDiv & MUL & CArithmeticExpression | CMulDiv & DIV & CArithmeticExpression
    }

    object CArithmeticExpression extends CNonTerminal
    {
        CArithmeticExpression := NUMBER | IDENTIFIER | CFunctionCall | LEFT_PARENTHESIS & CExpression & RIGHT_PARENTHESIS
    }

    object CFunctionDefinition extends CNonTerminal
    {
        CFunctionDefinition :=
            IDENTIFIER & COLON & CFlowUnit |
            IDENTIFIER & COLON & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            IDENTIFIER & CFunctionParameter & CFlowUnit |
            IDENTIFIER & CFunctionParameter & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            UNDERSCORE & COLON & CFlowUnit |
            UNDERSCORE & COLON & LEFT_BRACKET & CFlow & RIGHT_BRACKET |
            UNDERSCORE & CFunctionParameter & CFlowUnit |
            UNDERSCORE & CFunctionParameter & LEFT_BRACKET & CFlow & RIGHT_BRACKET
    }

    object CFunctionParameter extends CNonTerminal
    {
        CFunctionParameter := IDENTIFIER | CFunctionParameter & COMMA & IDENTIFIER
    }    
    
    object CFunctionCall extends CNonTerminal
    {
        CFunctionCall :=
            IDENTIFIER & LEFT_PARENTHESIS & RIGHT_PARENTHESIS |
            IDENTIFIER & LEFT_PARENTHESIS & CExpression & RIGHT_PARENTHESIS |
            IDENTIFIER & LEFT_PARENTHESIS & CFunctionExpression & RIGHT_PARENTHESIS
    }

    object CFunctionExpression extends CNonTerminal
    {
        CFunctionExpression := CExpression & COMMA & CExpression | CFunctionExpression & COMMA & CExpression
    }

    val TERMINALS:Seq[CTerminal] = CLexemeType map LexemeType2Terminal toSeq

    val START = CProgram

    val NON_TERMINALS:Seq[CNonTerminal] =
    {
        val rslt = mutable.HashSet[CNonTerminal]()

        def walk(nonTerminal:CNonTerminal)
        {
            rslt.add(nonTerminal)
            nonTerminal.alternatives foreach(a => a.view
                filter (c => c.isInstanceOf[CNonTerminal] && (rslt contains c.asInstanceOf[CNonTerminal] unary_!))
                map (_.asInstanceOf[CNonTerminal])
                foreach walk)
        }

        walk(START)

        rslt.toSeq
    }

    val PRIORITIES:collection.Map[(CCategory, CCategory), CCategory] = mutable.HashMap[(CCategory, CCategory), CCategory](
        NON_TERMINALS flatMap (nt => nt.priorities flatMap (p => (nt, p) -> nt :: (p, nt) -> nt :: Nil)) :_*
    )

    private val lexemeTypeToTerminalMap:collection.Map[CLexemeType, CTerminal] = collection.mutable.HashMap(
        TERMINALS.view map (l => l.lexemeType -> l) :_*
    )
}
